/*
 *
 * This is a VoIP Application designed for local-network usage for voice and
 * messaging
 * Copyright (C) 2012  Imran M Yousuf (imyousuf@smitsol.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.smitsol.voip.server;

import com.smitsol.voip.api.domains.Binding;
import com.smitsol.voip.api.domains.BindingRegistry;
import com.smitsol.voip.server.sip.Headers;
import com.smitsol.voip.server.sip.SipUtils;
import com.smitsol.voip.server.sip.SipAuthenticator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.ListIterator;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.sip.Address;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.URI;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

/**
 *
 * @author imyousuf
 */
public class SmartVoIPRegistrarServlet extends AbstractSmartVoIPSipServlet {

  // Local
  private SipAuthenticator authenticator;

  @Override
  public void init() throws ServletException {
    super.init();
    authenticator = new SipAuthenticator(getAPI());
  }

  @Override
  protected void doRegister(SipServletRequest ssr) throws ServletException, IOException {
    logger.info("Received REGISTER request");
    final URI toUri = ssr.getTo().getURI();
    if (!toUri.isSipURI()) {
      ssr.createResponse(SipServletResponse.SC_NOT_IMPLEMENTED).send();
      return;
    }
    final String toUserUri = SipUtils.getCanonicalUri(toUri);
    final String toUserHost = ((SipURI) toUri).getHost();
    // TODO: Check REGISTER for domain is allowed
    logger.info("Received REGISTER request for " + toUserUri);
    // Send 401 if Authorization Header is not present
    if (ssr.getHeader(Headers.AUTHORIZATION) == null) {
      // Store 'Call-Id + AoR' for later retrieval of authentication request
      // values needed for authentication verification
      SipServletResponse authResponse = ssr.createResponse(SipServletResponse.SC_UNAUTHORIZED);
      authResponse.addHeader(Headers.WWW_AUTHENTICATE, authenticator.getAuntehicationHeaderValue());
      authResponse.send();
      logger.info("Sent 401 to " + authResponse.getTo().toString());
    }
    // If Authorization present authenticate user
    else {
      // Authenticate
      String username = authenticator.isAuthentic(ssr.getHeaders(Headers.AUTHORIZATION), ssr.getRequestURI().toString(),
                                                  ssr.getMethod());
      if (username != null) {
        // TODO: If de-registration request
        // TODO: Remove 'Contact' from user's AoR registry
        // TODO: If not de-registration request      
        final SipServletResponse okResponse;
        // Add this binding itself
        Address contactHeader = ssr.getAddressHeader(Headers.CONTACT);
        int specifiedExpires = NumberUtils.toInt(ssr.getHeader(Headers.EXPIRES), 0);
        if (contactHeader != null) {
          // TODO: Register AoR against ('Contact' + 'User'); so that 'Contact' device
          //       can be stored sorted                              
          BindingRegistry bindingRegistry = new BindingRegistry();
          bindingRegistry.setUsername(username);
          bindingRegistry.setAddressOfRecord(toUserUri);
          Binding binding = new Binding();
          binding.setContactUri(SipUtils.getCanonicalUri(contactHeader.getURI(), false));
          binding.setContactHeader(contactHeader.getURI().toString());
          // TODO: Default expiry period should come from configuration
          int defaultMinExpiryPeriod = 3600;
          final int expiryPeriod = Math.max(specifiedExpires, defaultMinExpiryPeriod);
          if (ssr.getExpires() != 0) {
            binding.setExpiryDateTime((long) ssr.getExpires());
          }
          else {
            binding.setExpiryDateTime((long) expiryPeriod * 1000);
          }
          binding.setBindingDateTime(new Date());
          binding.setBindingCallId(ssr.getCallId());
          binding.setAddressOfRecord(toUserUri);
          String cSeqHeader = ssr.getHeader(Headers.C_SEQUENCE);
          if (StringUtils.isNotBlank(cSeqHeader)) {
            String[] seps = cSeqHeader.split(" ");
            if (seps != null && seps.length > 0) {
              binding.setCseq(NumberUtils.toInt(seps[0], 1));
            }
          }
          if (binding.getCseq() <= 0) {
            binding.setCseq(1);
          }
          if (bindingRegistry.getBindings() == null) {
            bindingRegistry.setBindings(new ArrayList<Binding>());
          }
          bindingRegistry.getBindings().add(binding);
          getAPI().getBindingRegistryService().registerBinding(bindingRegistry);
          okResponse = ssr.createResponse(SipServletResponse.SC_OK);
          contactHeader.setExpires(expiryPeriod);
          okResponse.addAddressHeader(Headers.CONTACT, contactHeader, true);
        }
        else {
          logger.warn("No Contact presented thus a BAD REQUEST");
          ssr.createResponse(SipServletResponse.SC_BAD_REQUEST).send();
          return;
        }
        // TODO: Fetch all existing bindings for the AoR and add it
        okResponse.send();
        logger.info("Authentication successful");
      }
      else {
        SipServletResponse authFailResponse = ssr.createResponse(SipServletResponse.SC_FORBIDDEN);
        authFailResponse.send();
      }
    }
  }

  @Override
  protected void doSubscribe(SipServletRequest ssr) throws ServletException, IOException {
    super.doSubscribe(ssr);
  }
}
